package com.gosh.flutter_video_info

import android.content.Context
import android.media.MediaCodecInfo
import android.media.MediaExtractor
import android.media.MediaFormat
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
import com.abedelazizshe.lightcompressorlibrary.VideoQuality
import com.abedelazizshe.lightcompressorlibrary.config.AppSpecificStorageConfiguration
import com.abedelazizshe.lightcompressorlibrary.config.CacheStorageConfiguration
import com.abedelazizshe.lightcompressorlibrary.config.Configuration
import com.abedelazizshe.lightcompressorlibrary.config.SharedStorageConfiguration
import com.abedelazizshe.lightcompressorlibrary.config.VideoResizer
import com.otaliastudios.transcoder.Transcoder
import com.otaliastudios.transcoder.TranscoderListener
import com.otaliastudios.transcoder.strategy.DefaultAudioStrategy
import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.io.File
import java.io.FileInputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.concurrent.Future

/** FlutterVideoInfoPlugin  */
class FlutterVideoInfoPlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
    EventChannel.StreamHandler {
    /// The MethodChannel that will the communication between Flutter and native Android
    ///
    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
    /// when the Flutter Engine is detached from the Activity
    private var channel: MethodChannel? = null
    private var eventChannel: EventChannel? = null
    private var eventSink: EventSink? = null
    private val mHandler = Handler(Looper.getMainLooper())

    var mContext: Context? = null

    private val dirName = "video_compress"
    private val format = SimpleDateFormat("yyyyMMdd_hhmmss")

    var currentTask: Future<Void>? = null

    override fun onAttachedToEngine(flutterPluginBinding: FlutterPluginBinding) {
        mContext = flutterPluginBinding.applicationContext

        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_video_info")
        channel?.setMethodCallHandler(this)

        eventChannel =
            EventChannel(flutterPluginBinding.binaryMessenger, "flutter_video_info_stream")
        eventChannel?.setStreamHandler(this)
    }

    override fun onDetachedFromEngine(binding: FlutterPluginBinding) {
        channel?.setMethodCallHandler(null)
        eventChannel?.setStreamHandler(null)

    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        if (call.method == "compressVideo") {
            compressVideo(call, result)
        } else if (call.method == "cancelCompressing") {
            cancelCompressVideo(call, result)
        } else if (call.method == "getVideoCodec") {
            val videoPath = call.arguments as String
            result.success(getVideoField(videoPath).mimeType)
        } else {
            result.notImplemented()
        }
    }

    override fun onListen(arguments: Any?, events: EventSink?) {
        eventSink = events
    }

    override fun onCancel(arguments: Any?) {
        eventSink = null
    }


    private fun initDir(): Boolean {
        // 获取外部文件目录
        val externalFilesDir = mContext!!.getExternalFilesDir(null)
            ?: // 处理外部存储不可用的情况
            return false // 或者使用 Environment.DIRECTORY_PICTURES 等

        // 定义要创建的目录名
        val newDir = File(externalFilesDir, dirName)

        // 创建目录
        if (!newDir.exists()) {
            return newDir.mkdirs()
        }
        return true
    }

    private fun getFileOutputPath(path: String): String {
        if (initDir()) {
            val externalFilesDir =
                mContext!!.getExternalFilesDir(null) // 或者使用 Environment.DIRECTORY_PICTURES 等
            if (externalFilesDir != null) {
                val newDir = File(externalFilesDir, dirName)
                val out = format.format(Date())
                return newDir.absolutePath + File.separator + "compress_" + out + ".mp4"
            }
        }
        return ""
    }



    private fun compressVideo(call: MethodCall, result: MethodChannel.Result) {
        val videoPath = call.arguments as String
        val savePath = getFileOutputPath(videoPath)
        if (savePath.isEmpty()) {
            result.success(null)
            return
        }

        val videoFile = File(videoPath)
        if (!videoFile.exists()) {
            result.success(null)
            return
        }

        val videoFileSize = videoFile.length()
        val videoField = getVideoField(videoPath)
        if (videoField.mimeType == "video/avc" && videoFileSize < 10 * 1024 * 1024) {
            result.success(null) //视频是H264编码格式并且小于10MB，不压缩
            return
        }

        cancelTask()
//        val builder = DefaultVideoStrategy
//            .atMost(960, 960)
//            .bitRateMode(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)
//            .keyFrameInterval(1f)
//            .frameRate(25)
//
//        if (videoField.bitRate > 1600 * 1024) {
//            builder.bitRate((1600 * 1024).toLong())
//        } else if (videoField.bitRate > 0) {
//            builder.bitRate(videoField.bitRate)
//        }
//        currentTask = Transcoder.into(savePath)
//            .addDataSource(videoPath)
//            .setVideoTrackStrategy(builder.build())
//            .setAudioTrackStrategy(DefaultAudioStrategy
//                .builder()
//                .sampleRate(44100)
//                .bitRate(128 * 1000)
//                .build())
//            .setListener(object : TranscoderListener {
//                override fun onTranscodeProgress(progress: Double) {
//                    updateProgress(progress)
//                }
//
//                override fun onTranscodeCompleted(successCode: Int) {
//                    updateProgress(1.0)
//                    result.success(savePath)
//                }
//
//                override fun onTranscodeCanceled() {
//                    result.success(null)
//                }
//
//                override fun onTranscodeFailed(exception: Throwable) {
//                    result.success(null)
//                }
//            }).transcode()


        //------------
        val videoName = "MyVideo-${System.currentTimeMillis()}.mp4"
        var videoBitrateInMbps : Float? = videoField.bitRate / 1000000f
        if (videoField.bitRate > 1600 * 1024) {
            videoBitrateInMbps = (1600 * 1024) / 1000000f
        }
        if( (videoBitrateInMbps?:0f) <= 0f){
            videoBitrateInMbps = null
        }

        VideoCompressor.start(
            context = mContext!!,
            uris = listOf(Uri.fromFile(File(videoPath))),
            isStreamable = false,
            storageConfiguration = AppSpecificStorageConfiguration(
                subFolderName = dirName
            ),
            configureWith = Configuration(
                quality = VideoQuality.MEDIUM,
                isMinBitrateCheckEnabled = false,
                videoBitrateInMbps = videoBitrateInMbps,
                disableAudio = false,
                resizer = VideoResizer.limitSize(960.0, 960.0),
                videoNames = listOf(videoName),
            ),
            listener = object : CompressionListener {
                override fun onProgress(index: Int, percent: Float) {
                    mHandler.post{
                        eventSink?.success(percent / 100)
                    }
                }

                override fun onStart(index: Int) {}

                override fun onSuccess(index: Int, size: Long, path: String?) {
                    result.success(path)
                }

                override fun onFailure(index: Int, failureMessage: String) {
                    Log.i("FlutterVideoInfo", "onFailure $failureMessage")
                    result.success(null)
                }

                override fun onCancelled(index: Int) {
                    mHandler.post{
                        result.success(null)
                    }
                }
            }
        )

    }

    private fun cancelCompressVideo(call: MethodCall, result: MethodChannel.Result) {
        cancelTask()
        VideoCompressor.cancel()
        result.success("Cancel Success")
    }

    private fun cancelTask() {
        if (currentTask != null) {
            currentTask!!.cancel(true)
            currentTask = null
        }
    }

    fun updateProgress(progress: Double) {
        eventSink?.success(progress)
    }


    private fun getVideoField(path: String): VideoField {
        val result = VideoField()
        var mExtractor: MediaExtractor? = null
        var mMetadata: MediaMetadataRetriever? = null

        try {
            val mStream = FileInputStream(path)

            mExtractor = MediaExtractor()
            mExtractor.setDataSource(mStream.fd)

            mMetadata = MediaMetadataRetriever()
            mMetadata.setDataSource(mStream.fd)

            var videoMediaFormat: MediaFormat? = null
            var audioMediaFormat: MediaFormat? = null
            val trackCount = mExtractor.trackCount
            for (i in 0 until trackCount) {
                val format = mExtractor.getTrackFormat(i)
                val mimeType = format.getString(MediaFormat.KEY_MIME)
                if(mimeType != null){
                    if (mimeType!!.startsWith("video/")) {
                        result.mimeType = mimeType
                        videoMediaFormat = format
                    } else if(mimeType!!.startsWith("audio/")){
                        audioMediaFormat = format
                    }
                }
            }

            if (videoMediaFormat != null && videoMediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
                result.bitRate = videoMediaFormat.getInteger(MediaFormat.KEY_BIT_RATE).toLong()
            } else {
                try {
                    var audioRate = 0L
                    if (audioMediaFormat != null) {
                        audioRate = 128 * 1000L
                        if (audioMediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
                            audioRate = audioMediaFormat.getInteger(MediaFormat.KEY_BIT_RATE).toLong()
                        }
                    }
                    val fileBitRate =
                        mMetadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE)!!
                            .toInt().toLong()

                    if(fileBitRate > audioRate * 3){
                        result.bitRate = fileBitRate - audioRate
                    } else if(fileBitRate > audioRate * 2){
                        result.bitRate = (fileBitRate * 0.8).toLong()
                    } else {
                        result.bitRate = fileBitRate
                    }
                    Log.i("FlutterVideoInfo", "result.bitRate ${result.bitRate} fileBitRate $fileBitRate audioRate $audioRate")
                } catch (ignore: NumberFormatException) {
                }
            }
        } catch (ignored: Exception) {
        } finally {
            try {
                mExtractor?.release()
            } catch (ignored: Exception) {
            }
            try {
                mMetadata?.release()
            } catch (ignored: Exception) {
            }
        }
        return result
    }

}
